/**
 * @author: akolonitsky
 * @date: 12/7/11 1:13 PM
 */
package model
{
    import flashx.textLayout.events.DamageEvent;
    import flashx.textLayout.events.StatusChangeEvent;

    import model.vo.ConfigVO;

    import model.vo.LogRecord;
    import model.vo.ReportVO;

    import org.kolonitsky.alexey.debug.DebugWrapper;
    import org.kolonitsky.alexey.mvcext.NotificationMap;
    import org.kolonitsky.alexey.utils.DateUtil;
    import org.puremvc.as3.patterns.proxy.Proxy;


    /**
     *  Analytic proxy store array of ReportVO to each user report.
     */
    public class AnalyticProxy extends ElfinSafeProxy
    {
        public static const NAME:String = "analyticProxy";

        public static const REPORT_UPDATED:String = "AnalyticReportUpdate";

        public static const NOT_ENOUGH_RECORDS:String = "notEnoughRecords_analyticProxy";

        public function AnalyticProxy(tags:Object /* of Strings */, currencies:Object, config:ConfigVO)
        {
            super(NAME, [], config);

            _tags = tags;
        }

        /**
         * Update report method update exists report and send UPDATE notification
         * or create new report if report with name title not exists.
         *
         * @param records is LogRecords list.
         * @param title is updated/created report name
         */
        public function updateReport(records:Array /* LogRecords */, title:String):void
        {
            if (records == null || records.length == 0)
            {
                sendNotification(NOT_ENOUGH_RECORDS);
                return;
            }

            var report:ReportVO = getReportByName(title);
            var n:int = records.length;
            var isNewReport:Boolean = !Boolean(report);

            log("AnalyticProxy " + (isNewReport ? "create" : "update") + " report " + title);

            if (isNewReport)
            {
                report = new ReportVO(title);
                (data as Array).push(report);
            }
            else
            {
                report.incomingBalance = new LogRecord("incoming balance");
                report.incomingStatistic = [];
                report.incomingUntaggedBalance = new LogRecord("untagged incoming balance");
                report.outgoingBalance = new LogRecord("outgoing balance");
                report.outgoingStatistic = [];
                report.outgoingUntaggedBalance = new LogRecord("untagged outgoing balance");
            }

            report.beginDate = (records[0] as LogRecord).date
            report.endDate = (records[n - 1] as LogRecord).date;

            if (report.beginDate.time >= report.endDate.time)
            {
                sendNotification(NOT_ENOUGH_RECORDS);
                return;
            }

            DateUtil.round(report.beginDate, DateUtil.MILISECONDS_PER_DAY);

            // create slot LogRecord
            report.log = new Array(report.daysCount);
            for (var i:int = 0, dayDate:Date; i < report.daysCount; i++)
            {
                dayDate = new Date(report.beginDate.time + i * DateUtil.MILISECONDS_PER_DAY)
                report.log[i] = new LogRecord(dayDate);
            }

            // fill slots
            for (i = 0; i < n; i++)
                collectStatistic(records[i] as LogRecord, report);

            // group and sort tagged statistic
            report.outgoingStatistic = groupStatistic(report.outgoingStatistic, report.outgoingBalance);

            sendNotification(REPORT_UPDATED, report);
        }


        //-------------------------------------------------------------------------
        //
        //  Private logic
        //
        //-------------------------------------------------------------------------

        private var _tags:Object = {};

        /**
         * Get report by name.
         * @param title
         * @return
         */
        private function getReportByName(title:String):ReportVO
        {
            // search report between exists
            var list:Array = data as Array;
            var n:int = list.length;
            for (var i:int = 0; i < n; i++)
                if ((list[i] as ReportVO).title == title)
                    return list[i];
            return null;
        }

        /**
         * Collect statistig by tags
         * @param statistic
         * @return
         */
        private function groupStatistic(statistic:Array, balance:LogRecord):Array
        {
            var result:Array = [];
            var percent:Number = 0.0;
            var otherBalance:LogRecord = new LogRecord("other");
            var tempRecord:LogRecord = null;

            for (var i:int = 0; i < statistic.length; i++)
            {
                tempRecord = statistic[i] as LogRecord;
                percent = getPercent(tempRecord);

                if (percent > 0.008)
                    result.push(tempRecord);
                else
                    otherBalance.addValue(tempRecord.value);
            }

            percent = getPercent(otherBalance);

            if (percent > 0.008)
                result.push(otherBalance);

            function getPercent(record:LogRecord):Number
            {
                var result:Number = record.amount / balance.amount;
                result = result < 0 ? result * -1 : result;
//                trace("INFO: " + record.comment + "\t" + (percent * 100).toFixed(1) + "%");
                return result;
            }

            return result.sort(compareLogRecords);
        }

        /**
         *
         * @param record
         * @param report
         */
        private function collectStatistic(record:LogRecord, report:ReportVO):void
        {
            // add record to normalized log
            var slotRecord:LogRecord = report.getRecordByDate(record.date)
            if (slotRecord)
                slotRecord.addValue(record.value);
            else
                DebugWrapper.trace("WARNING: Slot not found date: " + record.date
                    + ", recordId: " + record.id
                    + ", comment: " + record.comment);

            //  collect report statistic info
            var tags:Array = record.tags;
            var balance:LogRecord = record.amount > 0 ? report.incomingBalance : report.outgoingBalance;
            var untaggedBalance:LogRecord = record.amount > 0 ? report.incomingUntaggedBalance : report.outgoingUntaggedBalance;
            var statistic:Array = record.amount > 0 ? report.incomingStatistic : report.outgoingStatistic;

            balance.addValue(record.value);

            if (tags == null || tags.length == 0)
                addRecordTo("", record, statistic);

            for (var i:int = 0; i < tags.length; i++)
                addRecordTo(tags[i], record, statistic)

        }

        private function addRecordTo(tag:String, record:LogRecord, statistic:Array):void
        {
            tag = tag || "untagged";
            for (var i:int = 0; i < statistic.length; i++)
            {
                var tagBalance:LogRecord = statistic[i] as LogRecord;
                if (tagBalance.comment == tag)
                    break;
            }

            if (i == statistic.length)
                statistic.push(new LogRecord(tag));

            (statistic[i] as LogRecord).addValue(record.value);
        }

        private function compareLogRecords(a:LogRecord, b:LogRecord):int
        {
            if (a.amount > b.amount) return 1;
            if (a.amount < b.amount) return -1;
            return 0;
        }
    }
}
